/*
 * Copyright 2007. Mount Sinai Hospital, Toronto, Canada.
 * 
 * Licensed under the GNU Lesser General Public License, Version 2. You
 * can find a copy of the license at:
 * 
 * http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt
 * 
 * IN NO EVENT SHALL MOUNT SINAI HOSPITAL BE LIABLE TO ANY PARTY FOR DIRECT, 
 * INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, INCLUDING LOST 
 * PROFITS, ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, 
 * EVEN IF MOUNT SINAI HOSPITAL HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH 
 * DAMAGE.
 * 
 * MOUNT SINAI HOSPITAL SPECIFICALLY DISCLAIMS ANY IMPLIED WARRANTIES OF 
 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE AND 
 * ACCOMPANYING DOCUMENTATION, IF ANY, PROVIDED HEREUNDER IS PROVIDED "AS IS". 
 * MOUNT SINAI HOSPITAL HAS NO OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, 
 * UPDATES, ENHANCEMENTS, OR MODIFICATIONS. 
 */
package com.sinai.mshab.client.ui.filter;

import java.util.ArrayList;
import java.util.Iterator;

import com.google.gwt.user.client.ui.ChangeListener;
import com.google.gwt.user.client.ui.ClickListener;
import com.google.gwt.user.client.ui.HorizontalPanel;
import com.google.gwt.user.client.ui.Label;
import com.google.gwt.user.client.ui.ListBox;
import com.google.gwt.user.client.ui.Widget;
import com.sinai.mshab.client.ui.Key;
import com.sinai.mshab.client.ui.aggregate.FormData;
import com.sinai.mshab.client.ui.aggregate.VerticalComponentList;

/**
 * This is an aggregation of filters. Every filter contained in here will share
 * the same table name.
 * 
 * This filter will collect all information about it's filters, and pass it on
 * to FilteredTable
 * 
 * @author serge
 * 
 */
public class FilterAggregate extends VerticalComponentList {

	private ListBox tables;

	private ArrayList filters, column_names;

	VerticalComponentList filters_panel;

	FilteredTable filtered_table;

	boolean empty = false;

	/**
	 * Creates a new filtered table aggregate, this aggregate contains filters
	 * for a specific table name.
	 * 
	 * @param t
	 */
	public FilterAggregate(FilteredTable t, boolean empty) {
		super("");
		this.empty = empty;

		filtered_table = t;

		filters = new ArrayList();
		column_names = new ArrayList();

		// ensure form data has the db structure
		FormData.getInstance().getDbStructure();

		// populating the tables
		tables = new ListBox();
		ArrayList table_names = FormData.getInstance().getTableNames();
		for (int i = 0; i < table_names.size(); i++) {
			String name = (String) table_names.get(i);
			if (!FormData.is_table_blacklisted(name)) {
				tables.addItem(name);
			}
		}
		tables.addChangeListener(new TableChangedListener());
		tables.addFocusListener(this);
		tables.setStyleName("drop_down");

		// creating labels.
		Label table_label = new Label("Category: ");
		table_label.setStyleName("description_label_short");

		// creating add filter button.
		Label add_filter_label = new Label("Add another criteria for this category?");
		add_filter_label.setStyleName("important_lightblue_small_clickable");
		add_filter_label.addClickListener(new AddFilterListener());

		// adding label - drop down to hpanel..
		HorizontalPanel hpanel = new HorizontalPanel();
		hpanel.add(table_label);
		hpanel.add(tables);

		// creating a vpanel to contain filter objects.
		filters_panel = new VerticalComponentList("");

		if (!empty) {
			add(hpanel);
		}
		add(filters_panel);

		// add css style to this widget.
		if (!empty) {
			setStyleName("lightshadebox");
			add(add_filter_label);
			addFilter();
		}
	}

	/**
	 * Returns the table name
	 * 
	 * @param returns
	 *            the selected table name
	 */
	public String getTableName() {
		String name;
		if (empty) {
			name = this.tableName;
		} else {
			name = tables.getItemText(tables.getSelectedIndex());
		}

		return name;
	}

	/**
	 * When to focus is lost validate the field.
	 */
	public void onFocus(Widget sender) {
		String name = ((ListBox) sender).getStyleName();
		((ListBox) sender).setStyleName(name + "_focus");
	}

	/**
	 * When to focus is lost validate the field.
	 */
	public void onLostFocus(Widget sender) {

		String name = ((ListBox) sender).getStyleName();
		if (name.indexOf("_focus") != -1) {
			((ListBox) sender).setStyleName(name.substring(0, name.length()
					- "_focus".length()));
		}
	}

	/**
	 * This listeners listens to changes in the tables drop down and changes the
	 * columns drop down appropriately.
	 */
	private class TableChangedListener implements ChangeListener {

		public void onChange(Widget sender) {
			filters_panel.clear();
			filters.clear();
			addFilter();
			filtered_table.updateColumns();
		}
	}

	/**
	 * This listener is attached to the button that registers new users.
	 */
	private class AddFilterListener implements ClickListener {
		public void onClick(Widget sender) {
			addFilter();
		}
	}

	/**
	 * Adds a new filter to this filter aggregate.
	 */
	private void addFilter() {
		Filter f = new Filter(getTableName(), filtered_table, this);
		filters_panel.add(f);
		filters.add(f);
		column_names.clear();
	}

	/**
	 * Adds a new filter to this filter aggregate.
	 */
	public void addFilter(BasicFilter f) {
		filters_panel.add((Widget) f);
		filters.add(f);
		column_names.clear();
	}

	/**
	 * Return the where clauses associated with this table
	 * 
	 * @return
	 */
	public String getColumns() {

		String result = "";

		ArrayList sofar = filtered_table.columns_seen_sofar;

		// walk through their filters and append their toString() with ANDs
		for (int i = 0; i < filters.size(); i++) {
			BasicFilter f = (BasicFilter) filters.get(i);

			String col_sql = getColumnSql(f);

			if (!sofar.contains(col_sql.trim())) {
				result += ", " + col_sql + " ";
				sofar.add(col_sql.trim());
			}
		}
		return result;
	}

	/**
	 * Return the where clauses associated with this table
	 * 
	 * @return
	 */
	public String getWheres() {

		String result = "";

		// walk through their filters and append their toString() with ANDs
		for (int i = 0; i < filters.size(); i++) {
			BasicFilter f = (BasicFilter) filters.get(i);

			if (i == 0) {
				result += " " + constructWhere(f) + " ";
			} else {
				result += " AND " + constructWhere(f) + " ";
			}
		}
		return result;
	}

	/**
	 * Returns the part of the column sql, the part of the SELECT clause.
	 * 
	 * @return the column sql, the part of the SELECT clause.
	 */
	public String getColumnSql(BasicFilter filter) {
		String full_col = getTableName() + "." + filter.getColumn();
		String show_col = getTableName() + "_" + filter.getColumn();

		return " " + full_col + " " + show_col + " ";
	}

	/**
	 * Returns the part of the WHERE clause that this filter contributes to
	 * 
	 * @return Returns the part of the WHERE clause that this
	 */
	public String constructWhere(BasicFilter filter) {

		String val = filter.getValue();
		String op = filter.getOperator();

		// sugar coating val, it it's a LIKE, put % % around val.
		if (op.equals("LIKE") || op.equals("NOT LIKE")) {
			val = "'%" + val + "%'";
		} else {
			val = "'" + val + "'";
		}

		return filter.getTableName() + "." + filter.getColumn() + " " + op
				+ " " + val;
	}

	/**
	 * Return the inner join clauses associated with this table
	 * 
	 * @return
	 */
	public String getJoin() {
		String result = "";
		Key key = FormData.getKey();
		String key_table = key.getTableName();

		Iterator it = key.getKeys().keySet().iterator();
		int counter = 0;
		while (it.hasNext()) {
			String column = (String) it.next();

			if (counter == 0) {
				result += this.getTableName() + " ON " + key_table + "."
						+ column + " = " + this.getTableName() + "." + column;
			} else {
				result += " AND " + key_table + "." + column + " = "
						+ this.getTableName() + "." + column;
			}

			counter++;
		}
		return result;
	}

	public void removeFilter(Filter filter) {
		filters.remove(filter);
		filters_panel.remove(filter);

		// if we have no more filters, remove ourselves from the filtered table
		if (filters.size() == 0) {
			filtered_table.removeAggregate(this);
		}
	}

	/**
	 * Set's the table to a specific value (assuming it's in the list)
	 * 
	 * @param string
	 *            the table name
	 */
	public void setTable(String string) {

		// finding and setting the right table name
		for (int i = 0; i < tables.getItemCount(); i++) {
			String current = tables.getItemText(i);
			if (current.equals(string)) {
				tables.setSelectedIndex(i);
			}
		}

		filters_panel.clear();
		filters.clear();
		addFilter();
	}

	/**
	 * Returns an arraylist of strings with currently present columns set in
	 * this aggregate
	 * 
	 * @return arraylist of strings with set column names.
	 */
	public ArrayList getColumnNames() {
		ArrayList columns = new ArrayList();

		for (int i = 0; i < filters.size(); i++) {
			columns.add(this.getTableName() + "."
					+ ((Filter) filters.get(i)).getColumn());
		}
		return columns;
	}
}
